﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace PathTool
{
    public class PathHelper
    {

        public static void GetWayPoints(Vector3[] points, int amountRadio, ref List<Vector3> wayPoints)
        {
            if (points == null || points.Length <= 1) { Debug.Log("points is empty!"); return; }
            if (wayPoints == null) { Debug.Log("wayPoints is empty"); return; }

            wayPoints.Clear();

            Vector3[] vector3s = PathControlPointGenerator(points);

            //Line Draw:
            Vector3 prevPt = Interp(vector3s, 0);
            int SmoothAmount = (points.Length - 1) * amountRadio;
            for (int i = 1; i <= SmoothAmount; i++)
            {
                float pm = (float)i / SmoothAmount;
                Vector3 currPt = Interp(vector3s, pm);

                //添加点
                wayPoints.Add(currPt);

                prevPt = currPt;
            }

        }


        public static void DrawPathHelper(Vector3[] path, Color color, string method, bool isClose = false)
        {
            Vector3[] vector3s = PathControlPointGenerator(path, isClose);

            //Line Draw:
            Vector3 prevPt = Interp(vector3s, 0);
            Gizmos.color = color;
            int SmoothAmount = path.Length * 20;
            for (int i = 1; i <= SmoothAmount; i++)
            {
                float pm = (float)i / SmoothAmount;
                Vector3 currPt = Interp(vector3s, pm);
                if (method == "gizmos")
                {
                    Gizmos.DrawLine(currPt, prevPt);
                }
                else if (method == "handles")
                {
                    Debug.LogError("iTween Error: Drawing a path with Handles is temporarily disabled because of compatability issues with Unity 2.6!");
                    //UnityEditor.Handles.DrawLine(currPt, prevPt);
                }
                prevPt = currPt;
            }
        }

        //andeeee from the Unity forum's steller Catmull-Rom class ( http://forum.unity3d.com/viewtopic.php?p=218400#218400 ):
        private class CRSpline
        {
            public Vector3[] pts;

            public CRSpline(params Vector3[] pts)
            {
                this.pts = new Vector3[pts.Length];
                Array.Copy(pts, this.pts, pts.Length);
            }


            public Vector3 Interp(float t)
            {
                int numSections = pts.Length - 3;
                int currPt = Mathf.Min(Mathf.FloorToInt(t * (float)numSections), numSections - 1);
                float u = t * (float)numSections - (float)currPt;
                Vector3 a = pts[currPt];
                Vector3 b = pts[currPt + 1];
                Vector3 c = pts[currPt + 2];
                Vector3 d = pts[currPt + 3];
                return .5f * ((-a + 3f * b - 3f * c + d) * (u * u * u) + (2f * a - 5f * b + 4f * c - d) * (u * u) + (-a + c) * u + 2f * b);
            }
        }

        private Vector3[] GenerateMoveToPathTargets(Vector3[] wayPoints)
        {
            Vector3[] suppliedPath = new Vector3[wayPoints.Length];

            Array.Copy(wayPoints, suppliedPath, wayPoints.Length);

            int offset;

            offset = 2;


            //build calculated path:
            Vector3[] vector3s = new Vector3[suppliedPath.Length + offset];

            offset = 1;

            //populate calculate path;
            Array.Copy(suppliedPath, 0, vector3s, offset, suppliedPath.Length);

            //populate start and end control points:
            //vector3s[0] = vector3s[1] - vector3s[2];
            vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]);
            vector3s[vector3s.Length - 1] = vector3s[vector3s.Length - 2] + (vector3s[vector3s.Length - 2] - vector3s[vector3s.Length - 3]);

            //is this a closed, continuous loop? yes? well then so let's make a continuous Catmull-Rom spline!
            if (vector3s[1] == vector3s[vector3s.Length - 2])
            {
                Vector3[] tmpLoopSpline = new Vector3[vector3s.Length];
                Array.Copy(vector3s, tmpLoopSpline, vector3s.Length);
                tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3];
                tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2];
                vector3s = new Vector3[tmpLoopSpline.Length];
                Array.Copy(tmpLoopSpline, vector3s, tmpLoopSpline.Length);
            }

            //create Catmull-Rom path:
            CRSpline path = new CRSpline(vector3s);

            return path.pts;

        }

        private static Vector3[] PathControlPointGenerator(Vector3[] path, bool isclose = false)
        {
            Vector3[] suppliedPath;
            Vector3[] vector3s;

            //create and store path points:
            if (isclose)
            {
                suppliedPath = new Vector3[path.Length + 1];
                Array.Copy(path, 0, suppliedPath, 0, path.Length);
                suppliedPath[suppliedPath.Length - 1] = path[0];
            }
            else
            {
                suppliedPath = path;
            }


            //populate calculate path;
            int offset = 2;
            vector3s = new Vector3[suppliedPath.Length + offset];
            Array.Copy(suppliedPath, 0, vector3s, 1, suppliedPath.Length);

            //populate start and end control points:
            //vector3s[0] = vector3s[1] - vector3s[2];
            vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]);
            vector3s[vector3s.Length - 1] = vector3s[vector3s.Length - 2] + (vector3s[vector3s.Length - 2] - vector3s[vector3s.Length - 3]);

            //is this a closed, continuous loop? yes? well then so let's make a continuous Catmull-Rom spline!
            if (vector3s[1] == vector3s[vector3s.Length - 2])
            {
                Vector3[] tmpLoopSpline = new Vector3[vector3s.Length];
                Array.Copy(vector3s, tmpLoopSpline, vector3s.Length);
                tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3];
                tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2];
                vector3s = new Vector3[tmpLoopSpline.Length];
                Array.Copy(tmpLoopSpline, vector3s, tmpLoopSpline.Length);
            }

            return (vector3s);
        }

        //andeeee from the Unity forum's steller Catmull-Rom class ( http://forum.unity3d.com/viewtopic.php?p=218400#218400 ):
        private static Vector3 Interp(Vector3[] pts, float t)
        {
            int numSections = pts.Length - 3;
            int currPt = Mathf.Min(Mathf.FloorToInt(t * (float)numSections), numSections - 1);
            float u = t * (float)numSections - (float)currPt;

            Vector3 a = pts[currPt];
            Vector3 b = pts[currPt + 1];
            Vector3 c = pts[currPt + 2];
            Vector3 d = pts[currPt + 3];

            return .5f * (
                (-a + 3f * b - 3f * c + d) * (u * u * u)
                + (2f * a - 5f * b + 4f * c - d) * (u * u)
                + (-a + c) * u
                + 2f * b
            );
        }

        /// <summary>
        /// Returns the length of a curved path drawn through the provided array of Vector3s.
        /// </summary>
        /// <returns>
        /// The length.
        /// </returns>
        /// <param name='path'>
        /// A <see cref="Vector3[]"/>
        /// </param>
        public static float PathLength(Vector3[] path)
        {
            //一个点的长度即为0
            if (path.Length <= 1) return 0;

            float pathLength = 0;

            Vector3[] vector3s = PathControlPointGenerator(path);

            //Line Draw:
            Vector3 prevPt = Interp(vector3s, 0);
            int SmoothAmount = path.Length * 20;
            for (int i = 1; i <= SmoothAmount; i++)
            {
                float pm = (float)i / SmoothAmount;
                Vector3 currPt = Interp(vector3s, pm);
                pathLength += Vector3.Distance(prevPt, currPt);
                prevPt = currPt;
            }

            return pathLength;
        }
    }
}

